//====================================================================================================
// Filename:	Repository.inl
// Created by:	Josh Blidook
//====================================================================================================

// Code from Slant 6, Peter thinks it might not be that good...

//====================================================================================================
// Includes
//====================================================================================================

#include "Precompiled.h"

#include "Repository.h"

//====================================================================================================
// Class Declarations
//====================================================================================================

template <typename T>
Repository<T>::Repository(Meta::Type _type, u16 capacity)
: RepositoryBase(_type)
{
	mData.reserve(capacity);
	mInstanceCount.resize(capacity, 0);
	mFreeSlots.reserve(capacity);
}

template <typename T>
ID Repository<T>::Allocate() // Scales well
{
	ASSERT(!mFreeSlots.empty() || mData.size() < mData.capacity(), "[Repository(type = %u)] Failed to allocate. Respository is full.", type);
	if(mFreeSlots.empty() && mData.size() >= mData.capacity())
	{
		return ID();
	}
	
	u16 index(0);
	if(!mFreeSlots.empty()) // .size > 0 are the same thing, but not as quick
	{
		index = mFreeSlots.back();
		mFreeSlots.pop_back();
	}
	else
	{
		index = (u16)mData.size();
		mData.push_back(T());
	}
	return ID(GetType(), mInstanceCount[index], index);
}

//----------------------------------------------------------------------------------------------------

template <typename T>
void Repository<T>::Free(ID& id)
{
	if(IsValid(id))
	{
		const u16 index = id.GetIndex();

		// Free Slot
		mData[index] = T();
		++mInstanceCount[index]; // This invalidates all existing IDs
		mFreeSlots.push_back(index);

		// Invalidate ID
		id.Invalidate();
	}
}

//----------------------------------------------------------------------------------------------------

template <typename T>
T& Repository<T>::GetItem(ID id) // Assumes this exists, Would call an ASSERT if this does not exist
{
	ASSERT(!mFreeSlots.empty() || mData.size() < mData.capacity(), "[Repository(type = %u)] Failed to get item with ID(%u, %u, %U).", mType, id.GetType(), id.GetInstance(), id.GetIndex());
	return mData[id.GetIndex()];
}

//----------------------------------------------------------------------------------------------------

template <typename T>
const T& Repository<T>::GetItem(ID id) const
{
	ASSERT(!mFreeSlots.empty() || mData.size() < mData.capacity(), "[Repository(type = %u)] Failed to get item with ID(%u, %u, %U).", mType, id.GetType(), id.GetInstance(), id.GetIndex());
	return mData[id.GetIndex()];
}

//----------------------------------------------------------------------------------------------------

template <typename T>
T* Repository<T>::FindItem(ID id) // Returns pointer
{
	T* item = nullptr;
	if(IsValid(id))
	{
		item = &mData[id.GetIndex()];
	}
	return item;
}

//----------------------------------------------------------------------------------------------------
template <typename T>
const T* Repository<T>::FindItem(ID id) const
{
	const T* item = nullptr;
	if(IsValid(id))
	{
		item = &mData[id.GetIndex()];
	}
	return item;
}

//----------------------------------------------------------------------------------------------------

template <typename T>
bool Repository<T>::IsValid(ID id) const
{
	const Meta::Type type(id.GetType());
	const u8 instance(id.GetInstance());
	const u16 index(id.GetIndex());

	return (type == GetType() && index < mData.size() && instance == mInstanceCount[index]);
}

//====================================================================================================
// Inline Declarations
//====================================================================================================